"""
    TicHackToe views file.

    This file contains the logic behind users interfacing with the system.
"""

from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.models import User
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
from django.shortcuts import render_to_response
from django.template import RequestContext
from django.utils import simplejson
from django.core.validators import validate_email
from django.core.validators import ValidationError
from django.views.decorators.csrf import csrf_exempt
import time

from TicHackToe.THT.models import THTGame, UserRecord, GameStatus

###################
# GLOBAL CONSTANTS
#####################
MAX_GAMES=10
######################
def displayUserLogin(request ):
    return render_to_response('login.html', {'error_message': ""},
                               context_instance=RequestContext(request))

@csrf_exempt
def userlogin(request):
    username = request.POST['username']
    password = request.POST['pwd']
    
    failed_attempt = request.session.get('failed_attempt', 0)
    last_failed_time = request.session.get('last_failed_time', 0)
    request_time = time.mktime(time.gmtime())
    
    if failed_attempt >= 3 and request_time - last_failed_time < 15 * 60:
        display = 'You have tried 3 times. Please try again 15 minutes later'
        return render_to_response('login.html', {'error_message': display},
                                    context_instance=RequestContext(request))
        
    if request_time - last_failed_time > 15 * 60:
        failed_attempt = 0
    
    user = authenticate(username=username, password=password)
    if user is not None:
        if user.is_active:
            print ("user is active")
            login(request, user)
            # Redirect to a success page.
            return main(request)
        else:
            # Return a 'disabled account' error message
            display = 'disabled account' 
            return render_to_response('login.html', {'error_message': display},
                                    context_instance=RequestContext(request))

    else:
        request.session['failed_attempt'] = failed_attempt + 1
        request.session['last_failed_time'] = request_time
        # Return an 'invalid login' error message.
        display = 'invalid login' 
        return render_to_response('login.html', {'error_message': display},
                                    context_instance=RequestContext(request))

def displayUserRegister(request):
    return render_to_response('register.html', {'error_message': ""},
                                    context_instance=RequestContext(request))
    
@csrf_exempt
def userRegister(request):
    username = request.POST['username']
    password = request.POST['pwd']
    password2 = request.POST['pwd2']
    email = request.POST['email']
    
    if password != password2:
        return render_to_response('register.html',
            {'error_message': "Passwords do not match."},
            context_instance=RequestContext(request))
    if len(username) < 4 or len(username) > 16:
        return render_to_response('register.html',
            {'error_message': "Username must be between 4 - 16 characters."},
            context_instance=RequestContext(request))
    if not username.isalnum():
        return render_to_response('register.html',
            {'error_message': "Username can only contain number and letters."},
            context_instance=RequestContext(request))
    if not password.isalnum():
        return render_to_response('register.html',
            {'error_message': "Password can only contain number and letters."},
            context_instance=RequestContext(request))
    if len(password) < 6 or len(password) > 16:
        return render_to_response('register.html',
            {'error_message': "Password must be between 6 - 16 characters."},
            context_instance=RequestContext(request))
    try:
        validate_email(email)
    except ValidationError:
        return render_to_response('register.html',
            {'error_message': "Invalid email address."},
            context_instance=RequestContext(request))
        
    try: 
        user = User.objects.get(username=username)
    except User.DoesNotExist:
        user = None
        
    if user is not None:
        return render_to_response('register.html',
            {'error_message': "Username is already used, please select a new "
            "username"}, context_instance=RequestContext(request))
    else : 
        user = User.objects.create_user(username, email, password) 
        user.save()
        user.userrecord_set.create()
        return displayUserLogin(request)
        

def get_games():
    """
    Helper Function!
    Returns a list of THTGame objects.

    If less than 10 games exist on the server then it will create
    new empty games to fill the list.
    """
    games = list(THTGame.objects.all()) ### DEFECT 4 and 5
    while len(games) < MAX_GAMES:
        new_game = THTGame()
        new_game.init_board()
        games.append(new_game)
        
    for game in games:
        if game.player_x_id and game.player_o_id:
            game.in_session=u'Game currently been played at this table.'
        else:
            game.in_session=u'Game is not currently in session at this table.'
            
    return games

def get_leader_board():
    """
    Helper Function!
    Returns the top 10 user records in descending order
    """
    all_records = list(UserRecord.objects.all())
    for record in all_records:
        record.score = record.get_score
    all_records.sort(key=lambda x: x.score, reverse=True)
    return all_records[:10]
        
@login_required
def main(request):
    print ("in main page")
    user = request.user
    display = u'Hello %s, welcome to TicHackToe! Enjoy your games!' % (
                                                                user.username)
    userRecord = UserRecord.objects.get(user=user)
    games = get_games()
    #reset inactive games if needed
    for g in games:
        g.check_activity()
    #get the updated game list
    games = get_games()
    
    top10 = get_leader_board()
    template_dict = {'welcome_message': display,
                     'user_record': userRecord,
                     'games': games,
                     'top10': top10}

    return render_to_response('main.html', template_dict,
                              context_instance=RequestContext(request))

@login_required
@csrf_exempt
def xhr_top10_request(request):
    print ('get top 10 list')
    if request.is_ajax() and request.method == 'POST':
        top10 = get_leader_board()
        a=[]
        for record in top10:
            entry={'name':record.user.username, 
                   'score':record.score,
                   'wins':record.wins,
                   'loss':record.loss,
                   'tie':record.tie}
            a.append(entry)
        return HttpResponse(simplejson.dumps(a))
    
    return HttpResponse('bad request, not post')
        
@login_required
@csrf_exempt
def xhr_refresh_gamelist(request):
    print('get table list')
    if request.is_ajax() and request.method == 'POST':
        games = get_games()
        a=[]
        for game in games:
            entry={'id':game.id,
                   'session':game.in_session,
                   'x_player':game.player_x_username,
                   'o_player':game.player_o_username}
            a.append(entry)
        return HttpResponse(simplejson.dumps(a))
    
    return HttpResponse('bad request, not post')
  
@login_required
@csrf_exempt
def make_move(request):
    """
    This function implements one of the opponents selecting where on the board
    to place their mark.
    The place needs to be unoccupied and will be saved in the db.
    parameters passed in through the POST:
    table_id - 1-indexed table number, 1-10
    Position - N, NE, NW, ...
    move - X or O
    at the end, will refresh the screen for the user.
    """      
    table = request.POST["table_id"]
    # the table id passed in is 1-indexed, convert to 0-indexed
    table = int(table) - 1 ### DEFECT 1
    position = request.POST["Position"] ### it's actually used in refresh()
    move = request.POST["move"]
    user = request.user
    games = get_games()
    
    # check that the table number is valid
    if table < 0 or table >= len(games):
        #invalid table number
        return render_to_response('game.html',
                                {'error_message': "internal error"},
                              context_instance=RequestContext(request))
    g = games[table]
    g.make_move(user, move)
    return refresh(request)

def remove_player(player, game_id=None):
    """
    Remove the specified player from all game objects except for the one
    specified by game_id.  Calling quit_game() ensures that if the player
    was at another table that has a game in progress he will forfeit the game
    accordingly.
    """
    if game_id:
        x_games = THTGame.objects.filter(player_x_id=player.id
                                ).exclude(id=game_id)
        o_games = THTGame.objects.filter(player_o_id=player.id
                                ).exclude(id=game_id)
    else:
        x_games = THTGame.objects.filter(player_x_id=player.id)
        o_games = THTGame.objects.filter(player_o_id=player.id)

    for game in x_games:
        game.quit_game(player)
    for game in o_games:
        game.quit_game(player)
    return

@login_required
@csrf_exempt
def game(request):
    """
    This function will display the game board to the user.
    The game status as well as the value for each board position are returned
    This function will also ensure that the user is sitting at only one board
    at a time.
    POST message parameters:
    table_id = 1-indexed table number
    Position = X or O
    game_id = a unique game id for this particular game
    """
    user = request.user
    table = request.POST["table_id"]
    # convert from 1-indexed to 0-indexed
    table = int(table) - 1 ### DEFECT 1
    position = request.POST["Position"]
    #game_id = request.POST["game_id"] ### 
    refresh = 1
    
    games = get_games()
    if table < 0 or table >= len(games):
        return render_to_response('game.html', 
                                  {'error_message': "internal error"},
                              context_instance=RequestContext(request))
    game = games[table]
    remove_player(user, game.id)
    errMsg = ""
    if not game.register_user(user.id, position):
        errMsg = "Position is already taken"
        position = u'-'
    
    board = game.board
    status = game.game_status()
    game_id = game.get_game_id()
   
    # convert the table id back to 1-indexed for the response         
    table = str(int(table) + 1)

    if "Game Over" in status:
        refresh = 0 # tells the UI to stop refreshing now, the game's over
    template_dict = {'error_message': errMsg,
                     'table': table,
                     'position': position,
                     'username': user.username,
                     'game_status': status,
                     'game_id': game_id,
                     'refresh': refresh,
                     'NW': board['NW'],
                     'N': board['N'],
                     'NE': board['NE'],
                     'W': board['W'],
                     'C': board['C'],
                     'E': board['E'],
                     'SW': board['SW'],
                     'S': board['S'],
                     'SE': board['SE']}
    return render_to_response('game.html', template_dict,
                              context_instance=RequestContext(request))


@login_required
@csrf_exempt
def quit_game(request):
    """
    This function implements the quit functionality so that a player 
    forfeits the game if it is still in progress, and then the main page
    is displayed.
    POST parameters:
    table_id = 1-indexed table id
    """
    table = request.POST["table_id"]
    table = int(table) - 1 ### DEFECT 1
    user = request.user
    games = get_games()
    game = games[table]
    # register this game as forfeit if it's still in progress
    game.quit_game(user)
    # return the html for the main screen
    return main(request)
    

@login_required
@csrf_exempt
def refresh(request):
    """
    This function implements the refresh that the UI uses to see if the status
    of the game has changed.
    POST message parameters:
    table_id = 1-indexed table number
    Position = X or O
    game_id = a unique game id for this particular game
    """
    errMsg = ""
    table = request.POST["table_id"]
    table = int(table) - 1 ### DEFECT 1
    position = request.POST["Position"]
    g_id = request.POST["game_id"]
    user = request.user
    
    games = get_games()
    if table < 0 or table >= len(games):
        return render_to_response('game.html',  
                                  {'error_message': "internal error"},
                              context_instance=RequestContext(request))
    game = games[table]
    board = game.board

    ### DEFECT 3
    # when a game is finished, its state is saved, keyed off of the unique
    # game id.  So, first, we check that table to see if the game had finished.
    # If there is an entry in the table, then it's done and we need to display
    # that.
    # If there is no entry, then the game is still in progress.
    try:
        stats = GameStatus.objects.get(game_id=g_id)
        # we found old status, game is over
        status = stats.get_status()
        refresh = 0
    except:
        # No status found in db, this game isn't over
        refresh = 1
        table = str(int(table) + 1)
        status = game.game_status()
        if "Game Over" in status:
            refresh = 0
    print status
    template_dict = {'error_message': errMsg,
                     'table': table,
                     'position': position,
                     'username': user.username,
                     'game_status': status,
                     'game_id': g_id,
                     'refresh': refresh,
                     'NW': board['NW'],
                     'N': board['N'],
                     'NE': board['NE'],
                     'W': board['W'],
                     'C': board['C'],
                     'E': board['E'],
                     'SW': board['SW'],
                     'S': board['S'],
                     'SE': board['SE']}
    
    return render_to_response('game.html', template_dict,
                              context_instance=RequestContext(request))

@login_required
def logoutUser(request):
    """
    This takes care of the user logout
    """
    if request.method == 'POST':
        print ("logoutUser")
        logout(request)
        return displayUserLogin(request)
    else:
        return "GET method not allowed"
    
